package com.sluggard.mp.mvc;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.sluggard.common.exception.BusinessException;
import com.sluggard.common.model.res.Result;
import com.sluggard.mp.model.entity.IdEntity;
import com.sluggard.mp.model.req.*;
import com.sluggard.mp.properties.MybatisPlusConfigProperties;
import com.sluggard.mp.utils.FunctionTools;
import com.sluggard.mp.utils.GenericUtil;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import lombok.SneakyThrows;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

public class BaseController<R extends BaseReq, T extends IdEntity, S extends IService<T>> implements ApplicationContextAware {

    private Class<T> entityClass;

//    private M mapper;
    protected S service;

    @Resource
    private MybatisPlusConfigProperties properties;

    @Operation(summary = "分页列表查询")
    @PostMapping("tables")
    public Result<Page<T>> tables(@Valid @RequestBody PageQueryReq pageQuery) {
        Page<T> page = Page.of(pageQuery.getPageNo(), pageQuery.getPageSize());

        // 参数构建
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        buildSortOptions(pageQuery.getSortOptions(), queryWrapper);
        buildSearchParams(pageQuery.getSearchParams(), queryWrapper);

        // 执行查询
        onTables(queryWrapper, page);

        return Result.ok(page);
    }

    @Operation(summary = "列表查询")
    @PostMapping("list")
    public Result<List<T>> list(@Valid @RequestBody ListQueryReq req) {
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        buildSortOptions(req.getSortOptions(), queryWrapper);
        buildSearchParams(req.getSearchParams(), queryWrapper);

        return Result.ok(onList(queryWrapper));
    }


    @Operation(summary = "详情查询")
    @PostMapping("show")
    public Result<T> show(@RequestBody BaseReq req) {
        if(req.getId() < 1){
            throw new BusinessException("数据不存在");
        }
        return Result.ok(loadEntity(req.getId()));
    }


    @Operation(summary = "新增或修改")
    @PostMapping("save")
    @SneakyThrows
    @Transactional(rollbackFor = Exception.class)
    public Result<T> save(@RequestBody R req) {
        T instance;
        // 初始化entity
        if(Objects.nonNull(req.getId())){
            instance = service.getById(req.getId());
            if (Objects.isNull(instance)) {
                throw new BusinessException("数据不存在");
            }
        }else {
            instance = entityClass.getDeclaredConstructor().newInstance();
        }

        beforeSave(req, instance);

        // 执行插入
        service.saveOrUpdate(instance);
        afterSave(req, instance);
        return Result.ok();
    }

    @Operation(summary = "删除")
    @PostMapping("delete")
    @Transactional(rollbackFor = Exception.class)
    public Result<Void> delete(@RequestBody IdsReq idsReq) {
        maxDeleteNumValidate(idsReq);
        onDelete(idsReq);
        service.removeBatchByIds(idsReq.getIds());
        return Result.ok("删除成功");
    }

    @Operation(summary = "删除")
    @PostMapping("deleteById")
    @Transactional(rollbackFor = Exception.class)
    public Result<Void> deleteById(@RequestBody IdReq idReq) {
        onDeleteById(idReq);
        service.removeById(idReq.getId());
        return Result.ok("删除成功");
    }

    @Operation(summary = "逻辑删除")
    @PostMapping("logicalDelete")
    @Transactional(rollbackFor = Exception.class)
    public Result<Void> logicalDelete(@RequestBody IdsReq idsReq) {
        maxDeleteNumValidate(idsReq);
        onLogicalDelete(idsReq);
        UpdateWrapper<T> updateWrapper = new UpdateWrapper<>();
        updateWrapper.in("id", idsReq.getIds());
        updateWrapper.set(properties.getLogicalDeleteColumn(), 1);
        service.update(updateWrapper);
        return Result.ok("删除成功");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Class<S> clazz = GenericUtil.getSuperGenericClass(this.getClass(), 2);
        this.entityClass = GenericUtil.getSuperGenericClass(this.getClass(), 1);
        this.service = applicationContext.getBean(clazz);
    }

    /**
     * 执行分页查询
     *
     * @param chainWrapper
     * @param page
     */
    protected void onTables(QueryWrapper<T> chainWrapper, Page<T> page) {
        service.page(page, chainWrapper);
    }

    /**
     * 执行列表查询
     *
     * @param queryWrapper
     * @return
     */
    protected List<T> onList(QueryWrapper<T> queryWrapper) {
        return service.list(queryWrapper);
    }

    protected T loadEntity(Long id) {
        T t = service.getById(id);
        if (Objects.isNull(t)) {
            throw new BusinessException("数据不存在");
        }
        return t;
    }

    /**
     * 执行插入之后执行
     *
     * @param req      请求参数
     * @param instance 实体参数
     */
    protected void afterSave(R req, T instance) {

    }

    /**
     * 执行插入之后执行
     *
     * @param req      请求参数
     * @param instance 实体参数
     */
    protected void beforeSave(R req, T instance) {
        BeanUtils.copyProperties(req, instance);
    }

    /**
     * 执行删除操作之前执行
     *
     * @param idsReq 删除ids
     */
    protected void onDelete(IdsReq idsReq) {

    }

    /**
     * 执行删除操作之前执行
     *
     * @param idReq 删除id
     */
    protected void onDeleteById(IdReq idReq) {

    }

    /**
     * 执行逻辑删除操作之前执行
     *
     * @param idsReq 删除ids
     */
    protected void onLogicalDelete(IdsReq idsReq) {

    }

    /**
     * 单次最大删除数量校验
     *
     * @param idsReq 删除ids
     */
    protected void maxDeleteNumValidate(IdsReq idsReq) {
        if (idsReq.getIds().size() > properties.getMaxDeleteNum()) {
            throw new BusinessException("选择数据不能超过：" + properties.getMaxDeleteNum());
        }
    }

    /**
     * 条件参数构建
     *
     * @param searchParams 查询参数
     * @param queryWrapper wrapper
     */
    protected void buildSearchParams(List<SearchParam> searchParams, QueryWrapper<T> queryWrapper) {
        for (SearchParam searchParam : searchParams) {
            execute(queryWrapper, searchParam);
        }
        try{
            Field field = this.entityClass.getSuperclass().getDeclaredField("deleted");
            queryWrapper.eq(field.getName(), 0);
        } catch (Exception ignored){
        }
    }

    /**
     * 条件参数构建
     *
     * @param searchParams 查询参数
     * @param queryWrapper wrapper
     */
    protected void buildSearchParams(List<SearchParam> searchParams, LambdaQueryChainWrapper<T> queryWrapper) {
        for (SearchParam searchParam : searchParams) {
            execute(queryWrapper, searchParam);
        }
        try{
            Field field = this.entityClass.getSuperclass().getDeclaredField("deleted");
            queryWrapper.eq(parseFunction(field.getName()), 0);
        } catch (Exception ignored){
        }
    }

    /**
     * 排序参数构建
     *
     * @param sortOptions  排序参数
     * @param chainWrapper wrapper
     */
    protected void buildSortOptions(List<SortOption> sortOptions, LambdaQueryChainWrapper<T> chainWrapper) {
        for (SortOption sortOption : sortOptions) {
            if (sortOption.getSort() == 0) {
                chainWrapper.orderByAsc(parseFunction(sortOption.getColumn()));
            } else {
                chainWrapper.orderByDesc(parseFunction(sortOption.getColumn()));
            }
        }
    }

    /**
     * 排序参数构建
     *
     * @param sortOptions  排序参数
     * @param queryWrapper wrapper
     */
    protected void buildSortOptions(List<SortOption> sortOptions, QueryWrapper<T> queryWrapper) {
        for (SortOption sortOption : sortOptions) {
            if (sortOption.getSort() == 0) {
                queryWrapper.orderByAsc(sortOption.getColumn());
            } else {
                queryWrapper.orderByDesc(sortOption.getColumn());
            }
        }
    }

    /**
     * 查询条件构造
     *
     * @param wrapper     wrapper
     * @param searchParam 查询参数
     */
    private void execute(LambdaQueryChainWrapper<T> wrapper, SearchParam searchParam) {
        switch (searchParam.getOperator()) {
            case EQ -> wrapper.eq(parseFunction(searchParam.getColumn()), searchParam.getValue());
            case NOT_EQ -> wrapper.ne(parseFunction(searchParam.getColumn()), searchParam.getValue());
            case LIKE -> wrapper.like(parseFunction(searchParam.getColumn()), searchParam.getValue());
            case LIKE_LEFT -> wrapper.likeLeft(parseFunction(searchParam.getColumn()), searchParam.getValue());
            case LIKE_RIGHT -> wrapper.likeRight(parseFunction(searchParam.getColumn()), searchParam.getValue());
            case IN -> wrapper.in(parseFunction(searchParam.getColumn()), (Collection<?>) searchParam.getValue());
            case NOT_IN ->
                    wrapper.notIn(parseFunction(searchParam.getColumn()), (Collection<?>) searchParam.getValue());
            case NOT_LIKE -> wrapper.notLike(parseFunction(searchParam.getColumn()), searchParam.getValue());
            case NOT_LIKE_LEFT -> wrapper.notLikeLeft(parseFunction(searchParam.getColumn()), searchParam.getValue());
            case NOT_LIKE_RIGHT -> wrapper.notLikeRight(parseFunction(searchParam.getColumn()), searchParam.getValue());
            case IS_NULL -> wrapper.isNull(parseFunction(searchParam.getColumn()));
            case IS_NOT_NULL -> wrapper.isNotNull(parseFunction(searchParam.getColumn()));
            case GE -> wrapper.ge(parseFunction(searchParam.getColumn()), searchParam.getValue());
            case LE -> wrapper.le(parseFunction(searchParam.getColumn()), searchParam.getValue());
            case GT -> wrapper.gt(parseFunction(searchParam.getColumn()), searchParam.getValue());
            case LT -> wrapper.lt(parseFunction(searchParam.getColumn()), searchParam.getValue());
            case BETWEEN ->
                    wrapper.between(parseFunction(searchParam.getColumn()), searchParam.getMin(), searchParam.getMax());
            case NOT_BETWEEN ->
                    wrapper.notBetween(parseFunction(searchParam.getColumn()), searchParam.getMin(), searchParam.getMax());
        }
    }

    /**
     * 查询条件构造
     *
     * @param wrapper     wrapper
     * @param searchParam 查询参数
     */
    private void execute(QueryWrapper<T> wrapper, SearchParam searchParam) {
        switch (searchParam.getOperator()) {
            case EQ -> wrapper.eq(searchParam.getColumn(), searchParam.getValue());
            case NOT_EQ -> wrapper.ne(searchParam.getColumn(), searchParam.getValue());
            case LIKE -> wrapper.like(searchParam.getColumn(), searchParam.getValue());
            case LIKE_LEFT -> wrapper.likeLeft(searchParam.getColumn(), searchParam.getValue());
            case LIKE_RIGHT -> wrapper.likeRight(searchParam.getColumn(), searchParam.getValue());
            case IN -> wrapper.in(searchParam.getColumn(), (Collection<?>) searchParam.getValue());
            case NOT_IN -> wrapper.notIn(searchParam.getColumn(), (Collection<?>) searchParam.getValue());
            case NOT_LIKE -> wrapper.notLike(searchParam.getColumn(), searchParam.getValue());
            case NOT_LIKE_LEFT -> wrapper.notLikeLeft(searchParam.getColumn(), searchParam.getValue());
            case NOT_LIKE_RIGHT -> wrapper.notLikeRight(searchParam.getColumn(), searchParam.getValue());
            case IS_NULL -> wrapper.isNull(searchParam.getColumn());
            case IS_NOT_NULL -> wrapper.isNotNull(searchParam.getColumn());
            case GE -> wrapper.ge(searchParam.getColumn(), searchParam.getValue());
            case LE -> wrapper.le(searchParam.getColumn(), searchParam.getValue());
            case GT -> wrapper.gt(searchParam.getColumn(), searchParam.getValue());
            case LT -> wrapper.lt(searchParam.getColumn(), searchParam.getValue());
            case BETWEEN -> wrapper.between(searchParam.getColumn(), searchParam.getMin(), searchParam.getMax());
            case NOT_BETWEEN -> wrapper.notBetween(searchParam.getColumn(), searchParam.getMin(), searchParam.getMax());
            case FIND_IN_SET -> wrapper.apply("FIND_IN_SET('"+searchParam.getValue()+"', "+searchParam.getColumn()+") ");
        }
    }

    /**
     * 将字段字符串装换为SFunction对象 适配mybatis plus
     *
     * @param column 字段名 驼峰式
     * @return
     */
    protected SFunction<T, ?> parseFunction(String column) {
        return FunctionTools.getSFunction(this.entityClass, column);
    }
}
